// 版权归Go作者所有。版权所有。
// 此源代码的使用受BSD样式的约束
// 可以在许可证文件中找到的许可证。

package loader

import (
	"bytes"
	"cmd/internal/bio"
	"cmd/internal/goobj"
	"cmd/internal/obj"
	"cmd/internal/objabi"
	"cmd/internal/sys"
	"cmd/link/internal/sym"
	"debug/elf"
	"fmt"
	"log"
	"math/bits"
	"os"
	"sort"
	"strings"
)

var _ = fmt.Print

// Sym封装了一个全局符号索引，用于标识特定的
// 围棋符号。0值符号对应于无效符号。
type Sym int

// Relocs封装给定符号上的重新定位集；一
// 此类型的实例由Loader Relocs（）方法返回。
type Relocs struct {
	rs []goobj.Reloc

	li uint32   // 我们正在检查其relocs的符号的局部索引
	r  *oReader // 用于包含包的对象读取器
	l  *Loader  // 装载机
}

// ExtReloc包含用于外部重新定位的有效负载。
type ExtReloc struct {
	Xsym Sym
	Xadd int64
	Type objabi.RelocType
	Size uint8
}

// Reloc持有一个“句柄”，用于从
// 对象文件。
type Reloc struct {
	*goobj.Reloc
	r *oReader
	l *Loader
}

func (rel Reloc) Type() objabi.RelocType     { return objabi.RelocType(rel.Reloc.Type()) &^ objabi.R_WEAK }
func (rel Reloc) Weak() bool                 { return objabi.RelocType(rel.Reloc.Type())&objabi.R_WEAK != 0 }
func (rel Reloc) SetType(t objabi.RelocType) { rel.Reloc.SetType(uint16(t)) }
func (rel Reloc) Sym() Sym                   { return rel.l.resolve(rel.r, rel.Reloc.Sym()) }
func (rel Reloc) SetSym(s Sym)               { rel.Reloc.SetSym(goobj.SymRef{PkgIdx: 0, SymIdx: uint32(s)}) }
func (rel Reloc) IsMarker() bool             { return rel.Siz() == 0 }

// Aux持有一个“句柄”，用于从数据库访问Aux符号记录
// 对象文件。
type Aux struct {
	*goobj.Aux
	r *oReader
	l *Loader
}

func (a Aux) Sym() Sym { return a.l.resolve(a.r, a.Aux.Sym()) }

// oReader是obj.Reader的包装器类型，以及一些
// 额外信息。
type oReader struct {
	*goobj.Reader
	unit         *sym.CompilationUnit
	version      int    // 静态符号的版本
	flags        uint32 // 从对象文件读取
	pkgprefix    string
	syms         []Sym    // Sym的全局索引，按局部索引索引
	pkg          []uint32 // PkgIdx引用包的索引（索引到loader.objs数组）
	ndef         int      // 缓存goobj.Reader.NSym（）
	nhashed64def int      // 缓存goobj.Reader.NHashed64Def（）
	nhasheddef   int      // cache goobj.Reader.NHashedDef（）
	objidx       uint32   // objs切片中此读取器的索引
}

// 定义的符号总数（包符号、哈希符号和
// 非包装符号）。
func (r *oReader) NAlldef() int { return r.ndef + r.nhashed64def + r.nhasheddef + r.NNonpkgdef() }

type objIdx struct {
	r *oReader
	i Sym // 起始索引
}

// objSym表示对象文件中的符号。这是一个元组
// 对象和符号的局部索引。
// 对于外部符号，objidx是l.extReader（EXTRABJ）的索引，
// s是它在有效载荷数组中的索引。
// {0，0}表示nil符号。
type objSym struct {
	objidx uint32 // 对象的索引（在l.objs数组中）
	s      uint32 // 局部索引
}

type nameVer struct {
	name string
	v    int
}

type Bitmap []uint32

// 设置第i位。
func (bm Bitmap) Set(i Sym) {
	n, r := uint(i)/32, uint(i)%32
	bm[n] |= 1 << r
}

// 取消设置第i位。
func (bm Bitmap) Unset(i Sym) {
	n, r := uint(i)/32, uint(i)%32
	bm[n] &^= (1 << r)
}

// 是否设置了第i位。
func (bm Bitmap) Has(i Sym) bool {
	n, r := uint(i)/32, uint(i)%32
	return bm[n]&(1<<r) != 0
}

// 返回位图的当前长度（以位为单位）。
func (bm Bitmap) Len() int {
	return len(bm) * 32
}

// 返回设置的位数。
func (bm Bitmap) Count() int {
	s := 0
	for _, x := range bm {
		s += bits.OnesCount32(x)
	}
	return s
}

func MakeBitmap(n int) Bitmap {
	return make(Bitmap, (n+31)/32)
}

// growBitmap确保指定的位图具有足够的容量，
// 如果需要，重新分配（大小加倍）。
func growBitmap(reqLen int, b Bitmap) Bitmap {
	curLen := b.Len()
	if reqLen > curLen {
		b = append(b, MakeBitmap(reqLen+1-curLen)...)
	}
	return b
}

type symAndSize struct {
	sym  Sym
	size uint32
}

// 加载程序加载新的对象文件并解析索引符号引用。
// None
// 关于全局符号索引空间布局的说明：
// None
// -在宿主对象文件之前读取Go对象文件；每个围棋对象
// read将其定义的包符号添加到全局索引空间。
// 尚未添加非包装符号。
// None
// -在loader.LoadNonpkgSyms中，添加非软件包定义的符号和
// 所有对象文件中对全局索引空间的引用。
// None
// -主机对象文件加载发生；宿主对象加载程序执行以下操作：
// 查找到的每个符号的名称/版本查找；这可能会结束
// 扩展外部符号索引空间范围。宿主对象
// loader使用SymbolBuilder将符号有效载荷存储在loader.payloads中。
// None
// -每个符号都获得一个唯一的全局索引。复制和
// 覆盖/覆盖符号，第二次（或更高）出现
// 符号的全局索引与第一次出现的全局索引相同。
type Loader struct {
	start       map[*oReader]Sym // 从对象文件映射到其开始索引
	objs        []objIdx         // 按起始索引排序（即objIdx.i）
	extStart    Sym              // 从该索引开始，符号是外部定义的
	builtinSyms []Sym            // 内置符号的全局索引

	objSyms []objSym // 全局索引映射到本地索引

	symsByName    [2]map[string]Sym // 将符号名称映射到索引，两个映射分别用于ABI0和ABIInternal
	extStaticSyms map[nameVer]Sym   // 外部定义的静态符号，按名称键入

	extReader    *oReader // 用于外部符号的虚拟或读取器
	payloadBatch []extSymPayload
	payloads     []*extSymPayload // 链接器的内容物化了外部符号
	values       []int64          // 符号值，由全局符号索引索引

	sects    []*sym.Section // 部分
	symSects []uint16       // 符号的节，指向节数组的索引

	align []uint8 // 符号2^N对齐，按全局索引索引

	deferReturnTramp map[Sym]bool // 该符号是否为回电的蹦床

	objByPkg map[string]uint32 // 将包路径映射到其Go对象读取器的索引

	anonVersion int // 最近分配的ext静态sym伪版本

	// 用于存储用于存储的数据的位图和其他侧结构
	// 符号标志/属性；这些将通过
	// 相应的加载器“AttrXXX”和“SetAttrXXX”方法。请
	// 有关这些方法的更多详细信息，请访问这些方法的评论
	// 特定标志或属性的语义/解释。
	attrReachable        Bitmap // 可访问符号，按全局索引索引
	attrOnList           Bitmap // “列表上”符号，按全局索引索引
	attrLocal            Bitmap // “本地”符号，按全局索引索引
	attrNotInSymbolTable Bitmap // “不在symtab中”符号，由全局idx索引
	attrUsedInIface      Bitmap // “用于接口”符号，由全局idx索引
	attrVisibilityHidden Bitmap // 隐藏符号，由ext sym索引索引
	attrDuplicateOK      Bitmap // dupOK符号，由ext sym索引索引
	attrShared           Bitmap // 共享符号，按ext sym索引索引
	attrExternal         Bitmap // 外部符号，由ext sym索引索引

	attrReadOnly         map[Sym]bool     // 此sym的只读数据
	attrSpecial          map[Sym]struct{} // “特殊”框架符号
	attrCgoExportDynamic map[Sym]struct{} // “cgo\u导出\u动态”符号
	attrCgoExportStatic  map[Sym]struct{} // “cgo\u导出\u静态”符号
	generatedSyms        map[Sym]struct{} // 生成其内容的符号

	// 符号的外部和子关系。
	// TODO:想一想仅仅拥有这些是否更有效
	// 作为extSymPayload上的字段（请注意，这不是可行的
	// 如果在链接器中的某个位置为
	// 非外部sym）。
	outer map[Sym]Sym
	sub   map[Sym]Sym

	dynimplib   map[Sym]string      // 存储Dynimplib符号属性
	dynimpvers  map[Sym]string      // 存储Dynimpvers符号属性
	localentry  map[Sym]uint8       // 存储Localentry符号属性
	extname     map[Sym]string      // 存储Extname符号属性
	elfType     map[Sym]elf.SymType // 存储elf类型符号属性
	elfSym      map[Sym]int32       // 存储elf sym symbol属性
	localElfSym map[Sym]int32       // 存储“本地”elf sym symbol属性
	symPkg      map[Sym]string      // 存储符号的包，或存储shlib派生符号的库
	plt         map[Sym]int32       // 存储pe对象的动态端口
	got         map[Sym]int32       // pe对象的存储
	dynid       map[Sym]int32       // 存储符号的Dynid

	relocVariant map[relocId]sym.RelocVariant // 存储变量relocs

	// 用于实施现场跟踪；在死码期间创建，如果
	// 已启用字段跟踪。Reachparent[K]包含
	// 触发将符号K标记为活动的符号。
	Reachparent []Sym

	// cgo导出按符号名记录cgo导出的符号。
	CgoExports map[string]Sym

	flags uint32

	hasUnknownPkgPath bool // 如果任何Go对象具有未知的包路径

	strictDupMsgs int // 启用FlagStrictDup时，strict dup警告/错误数

	elfsetstring elfsetstringFunc

	errorReporter *ErrorReporter

	npkgsyms    int // 用于核算的包装符号数量
	nhashedsyms int // 哈希符号的数量，用于记帐
}

const (
	pkgDef = iota
	hashed64Def
	hashedDef
	nonPkgDef
	nonPkgRef
)

// objidx
const (
	nilObj = iota
	extObj
	goObjStart
)

type elfsetstringFunc func(str string, off int)

// extSymPayload保存链接器的有效负载（数据+重新定位）
// 外部符号（请注意，符号值存储在单独的片中）。
type extSymPayload struct {
	name   string // TODO:作为str表中的偏移量，这会更好吗？
	size   int64
	ver    int
	kind   sym.SymKind
	objidx uint32 // 如果由cloneToExternal制作sym，则原始对象的索引
	relocs []goobj.Reloc
	data   []byte
	auxs   []goobj.Aux
}

const (
	// Loader.flags
	FlagStrictDups = 1 << iota
	FlagUseABIAlias
)

func NewLoader(flags uint32, elfsetstring elfsetstringFunc, reporter *ErrorReporter) *Loader {
	nbuiltin := goobj.NBuiltin()
	extReader := &oReader{objidx: extObj}
	ldr := &Loader{
		start:                make(map[*oReader]Sym),
		objs:                 []objIdx{{}, {extReader, 0}}, // 零符号保留索引0，外部符号保留索引1
		objSyms:              make([]objSym, 1, 1),         // 这将在以后被覆盖。
		extReader:            extReader,
		symsByName:           [2]map[string]Sym{make(map[string]Sym, 80000), make(map[string]Sym, 50000)}, // 为ABI0预先分配~2MB，为ABI1符号预先分配~1MB
		objByPkg:             make(map[string]uint32),
		outer:                make(map[Sym]Sym),
		sub:                  make(map[Sym]Sym),
		dynimplib:            make(map[Sym]string),
		dynimpvers:           make(map[Sym]string),
		localentry:           make(map[Sym]uint8),
		extname:              make(map[Sym]string),
		attrReadOnly:         make(map[Sym]bool),
		elfType:              make(map[Sym]elf.SymType),
		elfSym:               make(map[Sym]int32),
		localElfSym:          make(map[Sym]int32),
		symPkg:               make(map[Sym]string),
		plt:                  make(map[Sym]int32),
		got:                  make(map[Sym]int32),
		dynid:                make(map[Sym]int32),
		attrSpecial:          make(map[Sym]struct{}),
		attrCgoExportDynamic: make(map[Sym]struct{}),
		attrCgoExportStatic:  make(map[Sym]struct{}),
		generatedSyms:        make(map[Sym]struct{}),
		deferReturnTramp:     make(map[Sym]bool),
		extStaticSyms:        make(map[nameVer]Sym),
		builtinSyms:          make([]Sym, nbuiltin),
		flags:                flags,
		elfsetstring:         elfsetstring,
		errorReporter:        reporter,
		sects:                []*sym.Section{nil}, // 零区段的储备指数0
	}
	reporter.ldr = ldr
	return ldr
}

// 添加对象文件r，返回开始索引。
func (l *Loader) addObj(pkg string, r *oReader) Sym {
	if _, ok := l.start[r]; ok {
		panic("already added")
	}
	pkg = objabi.PathToPrefix(pkg) // 对象文件包含转义的包路径
	if _, ok := l.objByPkg[pkg]; !ok {
		l.objByPkg[pkg] = r.objidx
	}
	i := Sym(len(l.objSyms))
	l.start[r] = i
	l.objs = append(l.objs, objIdx{r, i})
	if r.NeedNameExpansion() && !r.FromAssembly() {
		l.hasUnknownPkgPath = true
	}
	return i
}

// 从对象文件中添加符号，返回全局索引。
// 如果符号已存在，则返回该符号的索引。
func (st *loadState) addSym(name string, ver int, r *oReader, li uint32, kind int, osym *goobj.Sym) Sym {
	l := st.l
	if l.extStart != 0 {
		panic("addSym called after external symbol is created")
	}
	i := Sym(len(l.objSyms))
	addToGlobal := func() {
		l.objSyms = append(l.objSyms, objSym{r.objidx, li})
	}
	if name == "" && kind != hashed64Def && kind != hashedDef {
		addToGlobal()
		return i // 未命名辅助符号
	}
	if ver == r.version {
		// 静态符号。添加其全局索引，但不添加
		// 添加到名称查找表，因为它不能
		// 由名称引用。
		addToGlobal()
		return i
	}
	switch kind {
	case pkgDef:
		// 定义的包符号不能相互重复。
		// 我们首先加载所有的包符号，因此不需要
		// 在这里检查dup。
		// 我们仍然将其添加到查找表中，因为它可能仍然是
		// 通过名称引用（例如通过linkname）。
		l.symsByName[ver][name] = i
		addToGlobal()
		return i
	case hashed64Def, hashedDef:
		// 散列（内容可寻址）符号。检查散列
		// 但不要添加到名称查找表中，因为它们不是
		// 由名称引用。也不需要进行覆盖
		// 选中，因为相同的哈希表示相同的内容。
		var checkHash func() (symAndSize, bool)
		var addToHashMap func(symAndSize)
		var h64 uint64        // 仅用于hashed64Def
		var h *goobj.HashType // 仅用于hashedDef
		if kind == hashed64Def {
			checkHash = func() (symAndSize, bool) {
				h64 = r.Hash64(li - uint32(r.ndef))
				s, existed := st.hashed64Syms[h64]
				return s, existed
			}
			addToHashMap = func(ss symAndSize) { st.hashed64Syms[h64] = ss }
		} else {
			checkHash = func() (symAndSize, bool) {
				h = r.Hash(li - uint32(r.ndef+r.nhashed64def))
				s, existed := st.hashedSyms[*h]
				return s, existed
			}
			addToHashMap = func(ss symAndSize) { st.hashedSyms[*h] = ss }
		}
		siz := osym.Siz()
		if s, existed := checkHash(); existed {
			// 内容哈希是从符号数据和重定位构建的。在
			// 对象文件中，符号数据可能并不总是包含尾随零，
			// e、 对于[5]int{1,2,3}和[100]int{1,2,3}，数据是相同的
			// （尽管大小不同）。
			// 此外，对于短符号，内容哈希是的标识函数
			// 8字节和尾随的零不会更改哈希值，例如。
			// 散列（“A”）==散列（“A\0\0\0”）。
			// 因此，当两个符号具有相同的散列时，我们需要使用具有
			// 更大的尺寸。
			if siz > s.size {
				// 新符号的大小较大，请使用新符号。重写索引映射。
				l.objSyms[s.sym] = objSym{r.objidx, li}
				addToHashMap(symAndSize{s.sym, siz})
			}
			return s.sym
		}
		addToHashMap(symAndSize{i, siz})
		addToGlobal()
		return i
	}

	// 非包（命名）符号。检查它是否已经存在。
	oldi, existed := l.symsByName[ver][name]
	if !existed {
		l.symsByName[ver][name] = i
		addToGlobal()
		return i
	}
	// 符号已存在
	if osym.Dupok() {
		if l.flags&FlagStrictDups != 0 {
			l.checkdup(name, r, li, oldi)
		}
		// 修复问题#47185——给定两个带有
		// 不同尺寸，喜欢较大尺寸的符号。看见
		// 还发行了"46653"。
		szdup := l.SymSize(oldi)
		sz := int64(r.Sym(li).Siz())
		if szdup < sz {
			// 新符号覆盖旧符号。
			l.objSyms[oldi] = objSym{r.objidx, li}
		}
		return oldi
	}
	oldr, oldli := l.toLocal(oldi)
	oldsym := oldr.Sym(oldli)
	if oldsym.Dupok() {
		return oldi
	}
	overwrite := r.DataSize(li) != 0
	if overwrite {
		// 新符号覆盖旧符号。
		oldtyp := sym.AbiSymKindToSymKind[objabi.SymKind(oldsym.Type())]
		if !(oldtyp.IsData() && oldr.DataSize(oldli) == 0) {
			log.Fatalf("duplicated definition of symbol %s, from %s and %s", name, r.unit.Lib.Pkg, oldr.unit.Lib.Pkg)
		}
		l.objSyms[oldi] = objSym{r.objidx, li}
	} else {
		// 旧符号覆盖新符号。
		typ := sym.AbiSymKindToSymKind[objabi.SymKind(oldsym.Type())]
		if !typ.IsData() { // 仅允许覆盖数据符号
			log.Fatalf("duplicated definition of symbol %s, from %s and %s", name, r.unit.Lib.Pkg, oldr.unit.Lib.Pkg)
		}
	}
	return oldi
}

// newExtSym使用指定的
// 名称/版本。
func (l *Loader) newExtSym(name string, ver int) Sym {
	i := Sym(len(l.objSyms))
	if l.extStart == 0 {
		l.extStart = i
	}
	l.growValues(int(i) + 1)
	l.growAttrBitmaps(int(i) + 1)
	pi := l.newPayload(name, ver)
	l.objSyms = append(l.objSyms, objSym{l.extReader.objidx, uint32(pi)})
	l.extReader.syms = append(l.extReader.syms, i)
	return i
}

// LookupOrCreateSym查找具有指定名称/版本的符号，
// 如果找到，则返回其Sym索引。如果查找失败，将创建一个新的外部
// 将创建Sym，并将其输入到查找表中，然后返回。
func (l *Loader) LookupOrCreateSym(name string, ver int) Sym {
	i := l.Lookup(name, ver)
	if i != 0 {
		return i
	}
	i = l.newExtSym(name, ver)
	static := ver >= sym.SymVerStatic || ver < 0
	if static {
		l.extStaticSyms[nameVer{name, ver}] = i
	} else {
		l.symsByName[ver][name] = i
	}
	return i
}

// AddCgoExport在l.CGoExport中记录cgo导出的符号。
// 此表用于确定要使用的正确Go符号ABI
// 解析来自主机对象（没有ABI）的引用。
func (l *Loader) AddCgoExport(s Sym) {
	if l.CgoExports == nil {
		l.CgoExports = make(map[string]Sym)
	}
	l.CgoExports[l.SymName(s)] = s
}

// LookupOrCreateCgoExport类似于LookupOrCreateSym，但如果
// 指示全局符号，它使用CgoExport表确定
// 要使用的适当符号版本（ABI）。版本必须为0或0
// 或静态符号版本。
func (l *Loader) LookupOrCreateCgoExport(name string, ver int) Sym {
	if ver >= sym.SymVerStatic {
		return l.LookupOrCreateSym(name, ver)
	}
	if ver != 0 {
		panic("ver must be 0 or a static version")
	}
	// 查找从Go导出的cgo符号。
	if s, ok := l.CgoExports[name]; ok {
		return s
	}
	// 否则，它只能是宿主对象中的一个符号。
	// 为其创建版本0符号。
	return l.LookupOrCreateSym(name, 0)
}

func (l *Loader) IsExternal(i Sym) bool {
	r, _ := l.toLocal(i)
	return l.isExtReader(r)
}

func (l *Loader) isExtReader(r *oReader) bool {
	return r == l.extReader
}

// 对于外部符号，在有效载荷数组中返回其索引。
// XXX结果实际上不是一个全局索引。我们（ab）使用Sym类型
// 所以我们不需要转换来访问位图。
func (l *Loader) extIndex(i Sym) Sym {
	_, li := l.toLocal(i)
	return Sym(li)
}

// 获取外部符号的新有效负载，并在中返回其索引
// 有效载荷数组。
func (l *Loader) newPayload(name string, ver int) int {
	pi := len(l.payloads)
	pp := l.allocPayload()
	pp.name = name
	pp.ver = ver
	l.payloads = append(l.payloads, pp)
	l.growExtAttrBitmaps()
	return pi
}

// getPayload返回指向
// 外部符号（如果符号具有有效负载）。如果
// 有问题的符号是假的（零或不是外部符号）。
func (l *Loader) getPayload(i Sym) *extSymPayload {
	if !l.IsExternal(i) {
		panic(fmt.Sprintf("bogus symbol index %d in getPayload", i))
	}
	pi := l.extIndex(i)
	return l.payloads[pi]
}

// allocPayload分配一个新的有效负载。
func (l *Loader) allocPayload() *extSymPayload {
	batch := l.payloadBatch
	if len(batch) == 0 {
		batch = make([]extSymPayload, 1000)
	}
	p := &batch[0]
	l.payloadBatch = batch[1:]
	return p
}

func (ms *extSymPayload) Grow(siz int64) {
	if int64(int(siz)) != siz {
		log.Fatalf("symgrow size %d too long", siz)
	}
	if int64(len(ms.data)) >= siz {
		return
	}
	if cap(ms.data) < int(siz) {
		cl := len(ms.data)
		ms.data = append(ms.data, make([]byte, int(siz)+1-cl)...)
		ms.data = ms.data[0:cl]
	}
	ms.data = ms.data[:siz]
}

// 将本地索引转换为全局索引。
func (l *Loader) toGlobal(r *oReader, i uint32) Sym {
	return r.syms[i]
}

// 将全局索引转换为本地索引。
func (l *Loader) toLocal(i Sym) (*oReader, uint32) {
	return l.objs[l.objSyms[i].objidx].r, l.objSyms[i].s
}

// 解析本地符号引用。返回全局索引。
func (l *Loader) resolve(r *oReader, s goobj.SymRef) Sym {
	var rr *oReader
	switch p := s.PkgIdx; p {
	case goobj.PkgIdxInvalid:
		// 非零X的{0，X}永远不是来自Go对象的有效sym引用。
		// 我们从外部对象窃取这个空间作为符号引用。
		// 在本例中，X只是全局索引。
		if l.isExtReader(r) {
			return Sym(s.SymIdx)
		}
		if s.SymIdx != 0 {
			panic("bad sym ref")
		}
		return 0
	case goobj.PkgIdxHashed64:
		i := int(s.SymIdx) + r.ndef
		return r.syms[i]
	case goobj.PkgIdxHashed:
		i := int(s.SymIdx) + r.ndef + r.nhashed64def
		return r.syms[i]
	case goobj.PkgIdxNone:
		i := int(s.SymIdx) + r.ndef + r.nhashed64def + r.nhasheddef
		return r.syms[i]
	case goobj.PkgIdxBuiltin:
		if bi := l.builtinSyms[s.SymIdx]; bi != 0 {
			return bi
		}
		l.reportMissingBuiltin(int(s.SymIdx), r.unit.Lib.Pkg)
		return 0
	case goobj.PkgIdxSelf:
		rr = r
	default:
		rr = l.objs[r.pkg[p]].r
	}
	return l.toGlobal(rr, s.SymIdx)
}

// reportMissingBuiltin在我们有
// 针对未找到定义的运行时内置项重新定位
// 构建运行时包时。典型的例子是
// “runtime.racefuncenter”--当前如果您执行以下操作
// None
// go build-gcflags=-race myprogram.go
// None
// 编译器将插入对内置runtime.racefuncenter的调用，
// 但用于链接的运行时版本实际上并不包含
// 该符号的定义。详情见第42396期。
// None
// 按照当前的实现，这是一个致命错误。这有缺点
// 在这种情况下，如果有多个缺少的内置项，则只会出现错误
// 举第一个例子。从好的方面来说，在这里终止链接
// 优势在于我们以后不会面临恐慌或崩溃的风险
// 由于使用0值目标进行R_调用重定位，链接器中出现on
// 符号。
func (l *Loader) reportMissingBuiltin(bsym int, reflib string) {
	bname, _ := goobj.BuiltinName(bsym)
	log.Fatalf("reference to undefined builtin %q from package %q",
		bname, reflib)
}

// 按名称查找符号，返回全局索引，如果未找到，则返回0。
// 这更像是Syms.ROLookup而不是Lookup——它不会创建
// 新符号。
func (l *Loader) Lookup(name string, ver int) Sym {
	if ver >= sym.SymVerStatic || ver < 0 {
		return l.extStaticSyms[nameVer{name, ver}]
	}
	return l.symsByName[ver][name]
}

// 检查重复符号的内容是否相同。
func (l *Loader) checkdup(name string, r *oReader, li uint32, dup Sym) {
	p := r.Data(li)
	rdup, ldup := l.toLocal(dup)
	pdup := rdup.Data(ldup)
	reason := "same length but different contents"
	if len(p) != len(pdup) {
		reason = fmt.Sprintf("new length %d != old length %d", len(p), len(pdup))
	} else if bytes.Equal(p, pdup) {
		// 对于BSS符号，我们还需要检查其大小，请参见第46653期。
		szdup := l.SymSize(dup)
		sz := int64(r.Sym(li).Siz())
		if szdup == sz {
			return
		}
		reason = fmt.Sprintf("different sizes: new size %d != old size %d",
			sz, szdup)
	}
	fmt.Fprintf(os.Stderr, "cmd/link: while reading object for '%v': duplicate symbol '%s', previous def at '%v', with mismatched payload: %s\n", r.unit.Lib, name, rdup.unit.Lib, reason)

	// 目前，允许DWARF子程序为
	// 自动生成的包装函数。发生了什么事
	// 这里是我们得到的不同行号的正式表格
	// 参数；我猜pos正在被继承
	// 从需要包装的地方开始。
	allowed := strings.HasPrefix(name, "go.info.go.interface") ||
		strings.HasPrefix(name, "go.info.go.builtin") ||
		strings.HasPrefix(name, "go.debuglines")
	if !allowed {
		l.strictDupMsgs++
	}
}

func (l *Loader) NStrictDupMsgs() int { return l.strictDupMsgs }

// 符号总数。
func (l *Loader) NSym() int {
	return len(l.objSyms)
}

// 定义的Go符号数。
func (l *Loader) NDef() int {
	return int(l.extStart)
}

// 可到达的符号数。
func (l *Loader) NReachableSym() int {
	return l.attrReachable.Count()
}

// SymNameLen返回符号名的长度，尽量不加载
// 名字。
func (l *Loader) SymNameLen(i Sym) int {
	// 对于外部符号，我们无能为力。
	if l.IsExternal(i) {
		return len(l.SymName(i))
	}
	r, li := l.toLocal(i)
	le := r.Sym(li).NameLen(r.Reader)
	if !r.NeedNameExpansion() {
		return le
	}
	// 只需加载符号名。我们不知道它会扩展到什么程度。
	return len(l.SymName(i))
}

// 返回第i个符号的原始（未修补）名称。
func (l *Loader) RawSymName(i Sym) string {
	if l.IsExternal(i) {
		pp := l.getPayload(i)
		return pp.name
	}
	r, li := l.toLocal(i)
	return r.Sym(li).Name(r.Reader)
}

// 返回第i个符号的（打补丁的）名称。
func (l *Loader) SymName(i Sym) string {
	if l.IsExternal(i) {
		pp := l.getPayload(i)
		return pp.name
	}
	r, li := l.toLocal(i)
	if r == nil {
		return "?"
	}
	name := r.Sym(li).Name(r.Reader)
	if !r.NeedNameExpansion() {
		return name
	}
	return strings.Replace(name, "\"\".", r.pkgprefix, -1)
}

// 返回第i个符号的版本。
func (l *Loader) SymVersion(i Sym) int {
	if l.IsExternal(i) {
		pp := l.getPayload(i)
		return pp.ver
	}
	r, li := l.toLocal(i)
	return int(abiToVer(r.Sym(li).ABI(), r.version))
}

func (l *Loader) IsFileLocal(i Sym) bool {
	return l.SymVersion(i) >= sym.SymVerStatic
}

// 如果此符号派生自，则IsFromAssembly返回true
// 由Go汇编程序生成的对象文件。
func (l *Loader) IsFromAssembly(i Sym) bool {
	if l.IsExternal(i) {
		return false
	}
	r, _ := l.toLocal(i)
	return r.FromAssembly()
}

// 返回第i个符号的类型。
func (l *Loader) SymType(i Sym) sym.SymKind {
	if l.IsExternal(i) {
		pp := l.getPayload(i)
		if pp != nil {
			return pp.kind
		}
		return 0
	}
	r, li := l.toLocal(i)
	return sym.AbiSymKindToSymKind[objabi.SymKind(r.Sym(li).Type())]
}

// 返回第i个符号的属性。
func (l *Loader) SymAttr(i Sym) uint8 {
	if l.IsExternal(i) {
		// 托多：做点什么？外部符号具有不同的属性表示形式。
		// 目前，使用的是ReflectMethod、NoSplit、GoType和Typelink，不能使用它们
		// 由外部符号设置。
		return 0
	}
	r, li := l.toLocal(i)
	return r.Sym(li).Flag()
}

// 返回第i个符号的大小。
func (l *Loader) SymSize(i Sym) int64 {
	if l.IsExternal(i) {
		pp := l.getPayload(i)
		return pp.size
	}
	r, li := l.toLocal(i)
	return int64(r.Sym(li).Siz())
}

// 对于可传递的符号，AttrReachable返回true
// 从入口点引用。不可触及的符号不可用
// 写入输出。
func (l *Loader) AttrReachable(i Sym) bool {
	return l.attrReachable.Has(i)
}

// SetAttrReach可设置符号的可达性属性（请参见
// 可访问）。
func (l *Loader) SetAttrReachable(i Sym, v bool) {
	if v {
		l.attrReachable.Set(i)
	} else {
		l.attrReachable.Unset(i)
	}
}

// 对于某些列表上的符号（例如
// 所有文本符号列表，或数据符号列表之一）
// 并进行咨询，以避免在列表中放置符号时出现错误
// 两次
func (l *Loader) AttrOnList(i Sym) bool {
	return l.attrOnList.Has(i)
}

// SetAttrOnList设置符号的“列表”属性（请参见
// 吸引子）。
func (l *Loader) SetAttrOnList(i Sym, v bool) {
	if v {
		l.attrOnList.Set(i)
	} else {
		l.attrOnList.Unset(i)
	}
}

// AttrLocal对于仅在中可见的符号返回true
// 正在链接的模块（可执行文件或共享库）。这个属性
// 应用于Thunk和某些其他链接器生成的符号。
func (l *Loader) AttrLocal(i Sym) bool {
	return l.attrLocal.Has(i)
}

// SetAttrLocal符号的“local”属性（请参见上文的AttrLocal）。
func (l *Loader) SetAttrLocal(i Sym, v bool) {
	if v {
		l.attrLocal.Set(i)
	} else {
		l.attrLocal.Unset(i)
	}
}

// AttrUsedInIface为中使用的类型符号返回true
// 接口。
func (l *Loader) AttrUsedInIface(i Sym) bool {
	return l.attrUsedInIface.Has(i)
}

func (l *Loader) SetAttrUsedInIface(i Sym, v bool) {
	if v {
		l.attrUsedInIface.Set(i)
	} else {
		l.attrUsedInIface.Unset(i)
	}
}

// SymAddr检查符号是否可访问，并返回其值。
func (l *Loader) SymAddr(i Sym) int64 {
	if !l.AttrReachable(i) {
		panic("unreachable symbol in symaddr")
	}
	return l.values[i]
}

// AttrNotInSymbolTable对于不应为的符号返回true
// 添加到最终生成的加载模块的符号表中。
func (l *Loader) AttrNotInSymbolTable(i Sym) bool {
	return l.attrNotInSymbolTable.Has(i)
}

// SetAttrNotInSymbolTable符号的“不在符号选项卡中”属性
// （请参见上面的属性符号表）。
func (l *Loader) SetAttrNotInSymbolTable(i Sym, v bool) {
	if v {
		l.attrNotInSymbolTable.Set(i)
	} else {
		l.attrNotInSymbolTable.Unset(i)
	}
}

// AttrVisibilityHidden symbols对于具有
// 可见性设置为STV_隐藏。它们在中国成为当地的象征
// 最终的可执行文件。仅在内部链接时相关
// 在ELF平台上。
func (l *Loader) AttrVisibilityHidden(i Sym) bool {
	if !l.IsExternal(i) {
		return false
	}
	return l.attrVisibilityHidden.Has(l.extIndex(i))
}

// SetAttributeVisibilityHidden设置一个属性的“隐藏可见性”属性
// 符号（请参见属性可见性隐藏）。
func (l *Loader) SetAttrVisibilityHidden(i Sym, v bool) {
	if !l.IsExternal(i) {
		panic("tried to set visibility attr on non-external symbol")
	}
	if v {
		l.attrVisibilityHidden.Set(l.extIndex(i))
	} else {
		l.attrVisibilityHidden.Unset(l.extIndex(i))
	}
}

// AttrDuplicateOK为可以出现在中的符号返回true
// 多个对象文件。
func (l *Loader) AttrDuplicateOK(i Sym) bool {
	if !l.IsExternal(i) {
		// TODO:如果这条路经常被采用，它会
		// 从对象中复制标志值可能更有意义
		// 在预加载期间，将其转换为更大的位图。
		r, li := l.toLocal(i)
		return r.Sym(li).Dupok()
	}
	return l.attrDuplicateOK.Has(l.extIndex(i))
}

// SetAttrDuplicateOK设置外部文件的“duplicate OK”属性
// 符号（请参见“确定”）。
func (l *Loader) SetAttrDuplicateOK(i Sym, v bool) {
	if !l.IsExternal(i) {
		panic("tried to set dupok attr on non-external symbol")
	}
	if v {
		l.attrDuplicateOK.Set(l.extIndex(i))
	} else {
		l.attrDuplicateOK.Unset(l.extIndex(i))
	}
}

// 对于使用-shared选项编译的符号，AttrShared返回true。
func (l *Loader) AttrShared(i Sym) bool {
	if !l.IsExternal(i) {
		// TODO:如果这条路经常被采用，它会
		// 将标志值从
		// 在预加载期间将对象转换为更大的位图。
		r, _ := l.toLocal(i)
		return r.Shared()
	}
	return l.attrShared.Has(l.extIndex(i))
}

// SetAttrShared设置外部数据库的“共享”属性
// 符号（请参见属性共享）。
func (l *Loader) SetAttrShared(i Sym, v bool) {
	if !l.IsExternal(i) {
		panic(fmt.Sprintf("tried to set shared attr on non-external symbol %d %s", i, l.SymName(i)))
	}
	if v {
		l.attrShared.Set(l.extIndex(i))
	} else {
		l.attrShared.Unset(l.extIndex(i))
	}
}

// 对于从主机加载的函数符号，AttrExternal返回true
// 对象文件。
func (l *Loader) AttrExternal(i Sym) bool {
	if !l.IsExternal(i) {
		return false
	}
	return l.attrExternal.Has(l.extIndex(i))
}

// SetAttrExternal设置主机对象的“外部”属性
// 符号（请参见外部属性）。
func (l *Loader) SetAttrExternal(i Sym, v bool) {
	if !l.IsExternal(i) {
		panic(fmt.Sprintf("tried to set external attr on non-external symbol %q", l.RawSymName(i)))
	}
	if v {
		l.attrExternal.Set(l.extIndex(i))
	} else {
		l.attrExternal.Unset(l.extIndex(i))
	}
}

// AttrSpecial对于没有其属性的符号返回true
// 地址（即值）由
// data.go:dodata（）&data.go:address（）。
func (l *Loader) AttrSpecial(i Sym) bool {
	_, ok := l.attrSpecial[i]
	return ok
}

// SetAttrSpecial设置符号的“特殊”属性（请参见
// （特别）。
func (l *Loader) SetAttrSpecial(i Sym, v bool) {
	if v {
		l.attrSpecial[i] = struct{}{}
	} else {
		delete(l.attrSpecial, i)
	}
}

// AttrCgoExportDynamic为已删除的符号返回true
// 通过“cgo_export_dynamic”编译器指令特别标记
// cgo编写（回应
func (l *Loader) AttrCgoExportDynamic(i Sym) bool {
	_, ok := l.attrCgoExportDynamic[i]
	return ok
}

// SetAttrCgoExportDynamic为符号设置“cgo_导出_动态”
// （请参见AttrCgoExportDynamic）。
func (l *Loader) SetAttrCgoExportDynamic(i Sym, v bool) {
	if v {
		l.attrCgoExportDynamic[i] = struct{}{}
	} else {
		delete(l.attrCgoExportDynamic, i)
	}
}

// AttrCgoExportStatic为已被删除的符号返回true
// 通过“cgo_导出_静态”指令特别标记
// 由cgo编写。
func (l *Loader) AttrCgoExportStatic(i Sym) bool {
	_, ok := l.attrCgoExportStatic[i]
	return ok
}

// SetAttrCgoExportStatic为符号设置“cgo_导出_静态”
// （请参见AttrCgoExportStatic）。
func (l *Loader) SetAttrCgoExportStatic(i Sym, v bool) {
	if v {
		l.attrCgoExportStatic[i] = struct{}{}
	} else {
		delete(l.attrCgoExportStatic, i)
	}
}

// 如果符号以前被标记为符号，则IsGeneratedSym返回true
// 通过SetIsGeneratedSym生成发电机符号。发电机的功能
// 符号保存在链接上下文中。
func (l *Loader) IsGeneratedSym(i Sym) bool {
	_, ok := l.generatedSyms[i]
	return ok
}

// SetIsGeneratedSym将符号标记为生成的符号。数据不应该被删除
// 存储在生成的符号中，并注册和调用函数
// 这些符号中的每一个。
func (l *Loader) SetIsGeneratedSym(i Sym, v bool) {
	if !l.IsExternal(i) {
		panic("only external symbols can be generated")
	}
	if v {
		l.generatedSyms[i] = struct{}{}
	} else {
		delete(l.generatedSyms, i)
	}
}

func (l *Loader) AttrCgoExport(i Sym) bool {
	return l.AttrCgoExportDynamic(i) || l.AttrCgoExportStatic(i)
}

// 对于基础数据为
// 通过只读mmap存储。
func (l *Loader) AttrReadOnly(i Sym) bool {
	if v, ok := l.attrReadOnly[i]; ok {
		return v
	}
	if l.IsExternal(i) {
		pp := l.getPayload(i)
		if pp.objidx != 0 {
			return l.objs[pp.objidx].r.ReadOnly()
		}
		return false
	}
	r, _ := l.toLocal(i)
	return r.ReadOnly()
}

// SetAttrReadOnly为符号设置“数据为只读”属性
// （请参见ATRREANDONLY）。
func (l *Loader) SetAttrReadOnly(i Sym, v bool) {
	l.attrReadOnly[i] = v
}

// 对于列为
// 其他外部符号的子符号。子/外部机制是
// 加载主体对象时使用（从主体对象加载节
// 成为常规链接器符号，并且符号位于
// 它们的部分）和用于在
// 在内部链接动态可执行文件。
// None
// 请注意，在链接器的后期阶段，我们将Outer设置为
// 容器符号C，但不设置子（C）。因此，我们有两个
// 不同的场景：
// None
// -外部符号覆盖其子符号的地址范围。
// 在这种情况下设置了Outer.Sub。
// -外部符号不转换地址范围。它是零号的
// 并且没有子符号。在这种情况下，内部符号是
// 实际上不是一个“子符号”。（狡猾！）
// None
// 此方法仅对第一个场景中的子符号返回TRUE。
// None
// FIXME：最好是放弃这个，并有一个更好的方法
// 表示容器符号。

func (l *Loader) AttrSubSymbol(i Sym) bool {
	// 我们不再显式存储这个属性——return
	// 基于子符号设置的值。
	o := l.OuterSym(i)
	if o == 0 {
		return false
	}
	return l.SubSym(o) != 0
}

// 请注意，我们在加载程序中没有“SetAttrSubSymbol”方法；
// 客户端应改为使用AddInteriorSym方法来建立
// 主体对象符号的包含关系。

// 返回第i个符号是否设置了ReflectMethod属性。
func (l *Loader) IsReflectMethod(i Sym) bool {
	return l.SymAttr(i)&goobj.SymFlagReflectMethod != 0
}

// 返回第i个符号是否为nosplit。
func (l *Loader) IsNoSplit(i Sym) bool {
	return l.SymAttr(i)&goobj.SymFlagNoSplit != 0
}

// 返回这是否为Go类型符号。
func (l *Loader) IsGoType(i Sym) bool {
	return l.SymAttr(i)&goobj.SymFlagGoType != 0
}

// 返回此符号是否应包含在typelink中。
func (l *Loader) IsTypelink(i Sym) bool {
	return l.SymAttr(i)&goobj.SymFlagTypelink != 0
}

// 返回此符号是否为itab符号。
func (l *Loader) IsItab(i Sym) bool {
	if l.IsExternal(i) {
		return false
	}
	r, li := l.toLocal(i)
	return r.Sym(li).IsItab()
}

// Return这是否是回电的蹦床。
func (l *Loader) IsDeferReturnTramp(i Sym) bool {
	return l.deferReturnTramp[i]
}

// 设置我是一个蹦床的回音。
func (l *Loader) SetIsDeferReturnTramp(i Sym, v bool) {
	l.deferReturnTramp[i] = v
}

// growValues增长用于存储符号值的切片。
func (l *Loader) growValues(reqLen int) {
	curLen := len(l.values)
	if reqLen > curLen {
		l.values = append(l.values, make([]int64, reqLen+1-curLen)...)
	}
}

// SymValue返回第i个符号的值。我是全球指数。
func (l *Loader) SymValue(i Sym) int64 {
	return l.values[i]
}

// SetSymValue设置第i个符号的值。我是全球指数。
func (l *Loader) SetSymValue(i Sym, val int64) {
	l.values[i] = val
}

// AddToSymValue将第i个符号的值相加。i是全局索引。
func (l *Loader) AddToSymValue(i Sym, val int64) {
	l.values[i] += val
}

// 返回第i个符号的符号内容。我是全球指数。
func (l *Loader) Data(i Sym) []byte {
	if l.IsExternal(i) {
		pp := l.getPayload(i)
		if pp != nil {
			return pp.data
		}
		return nil
	}
	r, li := l.toLocal(i)
	return r.Data(li)
}

// FreeData清除外部符号的符号数据，允许内存
// 我想早点被释放。非外部符号无op。
// 我是全球指数。
func (l *Loader) FreeData(i Sym) {
	if l.IsExternal(i) {
		pp := l.getPayload(i)
		if pp != nil {
			pp.data = nil
		}
	}
}

// SYMLIGN返回符号的对齐方式。
func (l *Loader) SymAlign(i Sym) int32 {
	if int(i) >= len(l.align) {
		// align被延迟地扩展——这就是所讨论的sym
		// 在现有切片的范围之外，则假定其
		// 尚未设置对齐方式。
		return 0
	}
	// TODO:返回特定于拱门的参数有意义吗
	// 是否根据截面类型对齐？例如，STEXT=>32，
	// SDATA=>1，等等？
	abits := l.align[i]
	if abits == 0 {
		return 0
	}
	return int32(1 << (abits - 1))
}

// 设置符号的对齐方式。
func (l *Loader) SetSymAlign(i Sym, align int32) {
	// 拒绝无意义的对齐。
	if align < 0 || align&(align-1) != 0 {
		panic("bad alignment value")
	}
	if int(i) >= len(l.align) {
		l.align = append(l.align, make([]uint8, l.NSym()-len(l.align))...)
	}
	if align == 0 {
		l.align[i] = 0
	}
	l.align[i] = uint8(bits.Len32(uint32(align)))
}

// SymValue返回第i个符号的部分。我是全球指数。
func (l *Loader) SymSect(i Sym) *sym.Section {
	if int(i) >= len(l.symSects) {
		// symSects被延迟扩展——这就是所讨论的sym
		// 在现有切片的范围之外，则假定其
		// 节尚未设置。
		return nil
	}
	return l.sects[l.symSects[i]]
}

// 设置第i个符号的截面。我是全球指数。
func (l *Loader) SetSymSect(i Sym, sect *sym.Section) {
	if int(i) >= len(l.symSects) {
		l.symSects = append(l.symSects, make([]uint16, l.NSym()-len(l.symSects))...)
	}
	l.symSects[i] = sect.Index
}

// growtSects增长用于存储符号节的切片。
func (l *Loader) growSects(reqLen int) {
	curLen := len(l.symSects)
	if reqLen > curLen {
		l.symSects = append(l.symSects, make([]uint16, reqLen+1-curLen)...)
	}
}

// NewSection创建一个新的（输出）节。
func (l *Loader) NewSection() *sym.Section {
	sect := new(sym.Section)
	idx := len(l.sects)
	if idx != int(uint16(idx)) {
		panic("too many sections created")
	}
	sect.Index = uint16(idx)
	l.sects = append(l.sects, sect)
	return sect
}

// SymDynImplib返回指定对象的“dynimplib”属性
// 符号，构成指定符号信息的一部分
// 在“cgo\u import\u dynamic”编译器指令上。
func (l *Loader) SymDynimplib(i Sym) string {
	return l.dynimplib[i]
}

// SetSymDynimplib设置符号的“dynimplib”属性。
func (l *Loader) SetSymDynimplib(i Sym, value string) {
	// 拒绝坏符号
	if i >= Sym(len(l.objSyms)) || i == 0 {
		panic("bad symbol index in SetDynimplib")
	}
	if value == "" {
		delete(l.dynimplib, i)
	} else {
		l.dynimplib[i] = value
	}
}

// SymDynimpvers返回指定对象的“dynimpvers”属性
// 符号，构成指定符号信息的一部分
// 在“cgo\u import\u dynamic”编译器指令上。
func (l *Loader) SymDynimpvers(i Sym) string {
	return l.dynimpvers[i]
}

// SetSymDynimpvers设置符号的“dynimpvers”属性。
func (l *Loader) SetSymDynimpvers(i Sym, value string) {
	// 拒绝坏符号
	if i >= Sym(len(l.objSyms)) || i == 0 {
		panic("bad symbol index in SetDynimpvers")
	}
	if value == "" {
		delete(l.dynimpvers, i)
	} else {
		l.dynimpvers[i] = value
	}
}

// SymExtname返回指定对象的“extname”值
// 象征
func (l *Loader) SymExtname(i Sym) string {
	if s, ok := l.extname[i]; ok {
		return s
	}
	return l.SymName(i)
}

// SetSymExtname设置符号的“extname”属性。
func (l *Loader) SetSymExtname(i Sym, value string) {
	// 拒绝坏符号
	if i >= Sym(len(l.objSyms)) || i == 0 {
		panic("bad symbol index in SetExtname")
	}
	if value == "" {
		delete(l.extname, i)
	} else {
		l.extname[i] = value
	}
}

// SymElfType返回先前记录的符号ELF类型
// （仅用于ldshlibsyms从共享库读取的符号）。
// 未为被链接或删除的包定义的符号设置
// 通过ldelf读取的符号（因此保留为elf.STT_NOTYPE）。
func (l *Loader) SymElfType(i Sym) elf.SymType {
	if et, ok := l.elfType[i]; ok {
		return et
	}
	return elf.STT_NOTYPE
}

// SetSymElfType设置符号的elf type属性。
func (l *Loader) SetSymElfType(i Sym, et elf.SymType) {
	// 拒绝坏符号
	if i >= Sym(len(l.objSyms)) || i == 0 {
		panic("bad symbol index in SetSymElfType")
	}
	if et == elf.STT_NOTYPE {
		delete(l.elfType, i)
	} else {
		l.elfType[i] = et
	}
}

// SymElfSym返回给定加载程序的ELF符号索引
// 符号，在ELF符号表生成期间分配。
func (l *Loader) SymElfSym(i Sym) int32 {
	return l.elfSym[i]
}

// SetSymElfSym为符号设置elf符号索引。
func (l *Loader) SetSymElfSym(i Sym, es int32) {
	if i == 0 {
		panic("bad sym index")
	}
	if es == 0 {
		delete(l.elfSym, i)
	} else {
		l.elfSym[i] = es
	}
}

// SymLocalElfSym返回给定加载程序的“本地”ELF符号索引
// 符号，在ELF符号表生成期间分配。
func (l *Loader) SymLocalElfSym(i Sym) int32 {
	return l.localElfSym[i]
}

// SetSymLocalElfSym设置符号的“本地”elf符号索引。
func (l *Loader) SetSymLocalElfSym(i Sym, es int32) {
	if i == 0 {
		panic("bad sym index")
	}
	if es == 0 {
		delete(l.localElfSym, i)
	} else {
		l.localElfSym[i] = es
	}
}

// SymPlt返回符号s的PLT偏移量。
func (l *Loader) SymPlt(s Sym) int32 {
	if v, ok := l.plt[s]; ok {
		return v
	}
	return -1
}

// SetPlt设置符号i的PLT偏移。
func (l *Loader) SetPlt(i Sym, v int32) {
	if i >= Sym(len(l.objSyms)) || i == 0 {
		panic("bad symbol for SetPlt")
	}
	if v == -1 {
		delete(l.plt, i)
	} else {
		l.plt[i] = v
	}
}

// SymGot返回符号s的GET偏移量。
func (l *Loader) SymGot(s Sym) int32 {
	if v, ok := l.got[s]; ok {
		return v
	}
	return -1
}

// SetGot设置符号i的GET偏移量。
func (l *Loader) SetGot(i Sym, v int32) {
	if i >= Sym(len(l.objSyms)) || i == 0 {
		panic("bad symbol for SetGot")
	}
	if v == -1 {
		delete(l.got, i)
	} else {
		l.got[i] = v
	}
}

// SymDynid返回指定符号的“dynid”属性。
func (l *Loader) SymDynid(i Sym) int32 {
	if s, ok := l.dynid[i]; ok {
		return s
	}
	return -1
}

// SetSymDynid设置符号的“dynid”属性。
func (l *Loader) SetSymDynid(i Sym, val int32) {
	// 拒绝坏符号
	if i >= Sym(len(l.objSyms)) || i == 0 {
		panic("bad symbol index in SetSymDynid")
	}
	if val == -1 {
		delete(l.dynid, i)
	} else {
		l.dynid[i] = val
	}
}

// DynIdSyms返回dynID设置为
// 有趣的（非默认）值。预计这将是一个相当大的挑战
// 小套。
func (l *Loader) DynidSyms() []Sym {
	sl := make([]Sym, 0, len(l.dynid))
	for s := range l.dynid {
		sl = append(sl, s)
	}
	sort.Slice(sl, func(i, j int) bool { return sl[i] < sl[j] })
	return sl
}

// SymGoType返回给定符号的“Gotype”属性（由
// 变量符号的Go编译器）。此版本依赖于
// 读取目标sym的辅助符号——这可能更快
// 方法是在预加载期间检查gotype并复制
// 结果显示在地图中（可能希望在某个点尝试此操作并查看
// 如果它有助于加快速度）。
func (l *Loader) SymGoType(i Sym) Sym {
	var r *oReader
	var auxs []goobj.Aux
	if l.IsExternal(i) {
		pp := l.getPayload(i)
		r = l.objs[pp.objidx].r
		auxs = pp.auxs
	} else {
		var li uint32
		r, li = l.toLocal(i)
		auxs = r.Auxs(li)
	}
	for j := range auxs {
		a := &auxs[j]
		switch a.Type() {
		case goobj.AuxGotype:
			return l.resolve(r, a.Sym())
		}
	}
	return 0
}

// SymUnit返回给定符号的编译单元（将
// 对于外部或链接器制造的符号，通常为nil）。
func (l *Loader) SymUnit(i Sym) *sym.CompilationUnit {
	if l.IsExternal(i) {
		pp := l.getPayload(i)
		if pp.objidx != 0 {
			r := l.objs[pp.objidx].r
			return r.unit
		}
		return nil
	}
	r, _ := l.toLocal(i)
	return r.unit
}

// SymPkg返回符号来源的包（用于
// 常规编译器生成Go符号），但在
// 带有“-linkshared”的建筑（当从
// 共享库），将保留库名称。
// 注意：这对应于sym.Symbol.File字段。
func (l *Loader) SymPkg(i Sym) string {
	if f, ok := l.symPkg[i]; ok {
		return f
	}
	if l.IsExternal(i) {
		pp := l.getPayload(i)
		if pp.objidx != 0 {
			r := l.objs[pp.objidx].r
			return r.unit.Lib.Pkg
		}
		return ""
	}
	r, _ := l.toLocal(i)
	return r.unit.Lib.Pkg
}

// SetSymPkg为符号设置包/库。这是
// 主要用于外部符号，特别是导入的符号
// 来自共享库。
func (l *Loader) SetSymPkg(i Sym, pkg string) {
	// 拒绝坏符号
	if i >= Sym(len(l.objSyms)) || i == 0 {
		panic("bad symbol index in SetSymPkg")
	}
	l.symPkg[i] = pkg
}

// SymLocalentry返回指定项目的“local entry”值
// 象征
func (l *Loader) SymLocalentry(i Sym) uint8 {
	return l.localentry[i]
}

// SetSymLocalentry设置符号的“本地条目”属性。
func (l *Loader) SetSymLocalentry(i Sym, value uint8) {
	// 拒绝坏符号
	if i >= Sym(len(l.objSyms)) || i == 0 {
		panic("bad symbol index in SetSymLocalentry")
	}
	if value == 0 {
		delete(l.localentry, i)
	} else {
		l.localentry[i] = value
	}
}

// 返回给定全局索引的辅助符号数。
func (l *Loader) NAux(i Sym) int {
	if l.IsExternal(i) {
		return 0
	}
	r, li := l.toLocal(i)
	return r.NAux(li)
}

// 将“句柄”返回到第i个符号的第j个辅助符号。
func (l *Loader) Aux(i Sym, j int) Aux {
	if l.IsExternal(i) {
		return Aux{}
	}
	r, li := l.toLocal(i)
	if j >= r.NAux(li) {
		return Aux{}
	}
	return Aux{r.Aux(li, j), r, l}
}

// GetFuncDwarfAuxSyms收集并返回辅助DWARF
// 与给定功能符号关联的符号。在
// 在引入加载器时，这完全是使用名称完成的
// 查找，例如，对于名为XYZ的函数，我们将查找
// go.info.XYZ等。
func (l *Loader) GetFuncDwarfAuxSyms(fnSymIdx Sym) (auxDwarfInfo, auxDwarfLoc, auxDwarfRanges, auxDwarfLines Sym) {
	if l.SymType(fnSymIdx) != sym.STEXT {
		log.Fatalf("error: non-function sym %d/%s t=%s passed to GetFuncDwarfAuxSyms", fnSymIdx, l.SymName(fnSymIdx), l.SymType(fnSymIdx).String())
	}
	if l.IsExternal(fnSymIdx) {
		// 目前的预期是，任何外部功能都将
		// 没有辅助符号。
		return
	}
	r, li := l.toLocal(fnSymIdx)
	auxs := r.Auxs(li)
	for i := range auxs {
		a := &auxs[i]
		switch a.Type() {
		case goobj.AuxDwarfInfo:
			auxDwarfInfo = l.resolve(r, a.Sym())
			if l.SymType(auxDwarfInfo) != sym.SDWARFFCN {
				panic("aux dwarf info sym with wrong type")
			}
		case goobj.AuxDwarfLoc:
			auxDwarfLoc = l.resolve(r, a.Sym())
			if l.SymType(auxDwarfLoc) != sym.SDWARFLOC {
				panic("aux dwarf loc sym with wrong type")
			}
		case goobj.AuxDwarfRanges:
			auxDwarfRanges = l.resolve(r, a.Sym())
			if l.SymType(auxDwarfRanges) != sym.SDWARFRANGE {
				panic("aux dwarf ranges sym with wrong type")
			}
		case goobj.AuxDwarfLines:
			auxDwarfLines = l.resolve(r, a.Sym())
			if l.SymType(auxDwarfLines) != sym.SDWARFLINES {
				panic("aux dwarf lines sym with wrong type")
			}
		}
	}
	return
}

// AddInteriorSym将“interior”设置为的内部符号
// 集装箱/有效载荷符号“集装箱”。内部符号不适用
// 它本身有数据，但在其内部为数据的子范围命名
// 容器符号。容器本身可能有名称，也可能没有名称。
// 此方法主要用于宿主对象
// 加载程序，以捕获
// 对象文件。在读取主机对象文件时，我们通常
// 遇到包含内容的静态节符号（例如：“.text”）
// 对于一组函数，然后是一系列ELF（或macho等）
// 符号表条目，每个条目都指向一个子节
// 相应容器符号的（偏移量和长度）。在内部
// 在go链接器中，我们为容器创建了一个loader.Sym（即
// 预期会有实际内容/有效负载），然后是一组
// 内部加载器。Sym是指容器的一部分。
func (l *Loader) AddInteriorSym(container Sym, interior Sym) {
	// 容器符号应具有内容/数据。
	// 注意：这个限制可能太严格了（这是可能的）
	// 想象一个内部符号指向的零尺寸容器
	// ),；如果我们遇到问题，可以放松或移除它
	// 触发此操作的奇怪主机对象。
	if l.SymSize(container) == 0 && len(l.Data(container)) == 0 {
		panic("unexpected empty container symbol")
	}
	// 容器的内部符号不应具有
	// 内容/数据或重新定位。
	if len(l.Data(interior)) != 0 {
		panic("unexpected non-empty interior symbol")
	}
	// 内部符号应位于符号表中。
	if l.AttrNotInSymbolTable(interior) {
		panic("interior symbol must be in symtab")
	}
	// 只允许一个级别的安全壳。
	if l.OuterSym(container) != 0 {
		panic("outer has outer itself")
	}
	// Interior sym不应该已经有兄弟姐妹。
	if l.SubSym(interior) != 0 {
		panic("sub set for subsym")
	}
	// 内部符号不应指向容器。
	if l.OuterSym(interior) != 0 {
		panic("outer already set for subsym")
	}
	l.sub[interior] = l.sub[container]
	l.sub[container] = interior
	l.outer[interior] = container
}

// OuterSym获取宿主对象加载符号的外部符号。
func (l *Loader) OuterSym(i Sym) Sym {
	// 修正：是否为isExternal添加检查？
	return l.outer[i]
}

// SubSym获取宿主对象加载符号的子符号。
func (l *Loader) SubSym(i Sym) Sym {
	// 注意--没有检查l.isExternal（），因为我非常确定
	// 链接器中后面的阶段为“type.”syms设置了subsym
	return l.sub[i]
}

// SetCarrierSym声明“c”是承运人或集装箱符号
// 为了“s”。载体符号在链接器中用作容器
// 对于子符号集合，其中
// 子符号被有效地连接起来，以形成
// 载体载体在输出符号表中有一个名称
// 而子符号名称不是。例如，Go编译器
// 编译时发出命名字符串符号（类型SGOSTRING）
// 包裹消除重复数据后，这些符号将收集到
// 通过给他们分配一个名为
// “go.string.*”（显示在
// 输出负载模块）。
func (l *Loader) SetCarrierSym(s Sym, c Sym) {
	if c == 0 {
		panic("invalid carrier in SetCarrierSym")
	}
	if s == 0 {
		panic("invalid sub-symbol in SetCarrierSym")
	}
	// 载波符号不应具有内容/数据。它是
	// 允许其具有非零尺寸（允许使用发电机
	// 符号）。
	if len(l.Data(c)) != 0 {
		panic("unexpected non-empty carrier symbol")
	}
	l.outer[s] = c
	// relocsym的foldSubSymbolOffset要求
	// 有一个单一的控制级别--在这里强制执行。
	if l.outer[c] != 0 {
		panic("invalid nested carrier sym")
	}
}

// 初始化可访问位图及其同级以运行死码传递。
func (l *Loader) InitReachable() {
	l.growAttrBitmaps(l.NSym() + 1)
}

type symWithVal struct {
	s Sym
	v int64
}
type bySymValue []symWithVal

func (s bySymValue) Len() int           { return len(s) }
func (s bySymValue) Swap(i, j int)      { s[i], s[j] = s[j], s[i] }
func (s bySymValue) Less(i, j int) bool { return s[i].v < s[j].v }

// SortSub遍历“s”的子符号并对其进行排序
// 通过增加价值使其就位。返回值是新的
// 指定外部符号的子符号。
func (l *Loader) SortSub(s Sym) Sym {

	if s == 0 || l.sub[s] == 0 {
		return s
	}

	// 首先使用切片对符号进行排序。在开关上使用稳定的排序
	// 可能有不止一个符号具有相同的值，
	// 以保存可复制的构建。
	sl := []symWithVal{}
	for ss := l.sub[s]; ss != 0; ss = l.sub[ss] {
		sl = append(sl, symWithVal{s: ss, v: l.SymValue(ss)})
	}
	sort.Stable(bySymValue(sl))

	// 然后将所需的任何更改应用于子贴图。
	ns := Sym(0)
	for i := len(sl) - 1; i >= 0; i-- {
		s := sl[i].s
		l.sub[s] = ns
		ns = s
	}

	// 更新外部符号的sub，然后返回
	l.sub[s] = sl[0].s
	return sl[0].s
}

// SortSyms按符号值对符号列表进行排序。
func (l *Loader) SortSyms(ss []Sym) {
	sort.SliceStable(ss, func(i, j int) bool { return l.SymValue(ss[i]) < l.SymValue(ss[j]) })
}

// 确保可访问位图及其同级具有足够的大小。
func (l *Loader) growAttrBitmaps(reqLen int) {
	if reqLen > l.attrReachable.Len() {
		// 这些由全局符号索引
		l.attrReachable = growBitmap(reqLen, l.attrReachable)
		l.attrOnList = growBitmap(reqLen, l.attrOnList)
		l.attrLocal = growBitmap(reqLen, l.attrLocal)
		l.attrNotInSymbolTable = growBitmap(reqLen, l.attrNotInSymbolTable)
		l.attrUsedInIface = growBitmap(reqLen, l.attrUsedInIface)
	}
	l.growExtAttrBitmaps()
}

func (l *Loader) growExtAttrBitmaps() {
	// 它们通过外部符号索引（例如l.EXIDEX（i））进行索引
	extReqLen := len(l.payloads)
	if extReqLen > l.attrVisibilityHidden.Len() {
		l.attrVisibilityHidden = growBitmap(extReqLen, l.attrVisibilityHidden)
		l.attrDuplicateOK = growBitmap(extReqLen, l.attrDuplicateOK)
		l.attrShared = growBitmap(extReqLen, l.attrShared)
		l.attrExternal = growBitmap(extReqLen, l.attrExternal)
	}
}

func (relocs *Relocs) Count() int { return len(relocs.rs) }

// At返回全局符号的第j次重新调整。
func (relocs *Relocs) At(j int) Reloc {
	if relocs.l.isExtReader(relocs.r) {
		return Reloc{&relocs.rs[j], relocs.r, relocs.l}
	}
	return Reloc{&relocs.rs[j], relocs.r, relocs.l}
}

// Relocs返回给定全局符号的Relocs对象。
func (l *Loader) Relocs(i Sym) Relocs {
	r, li := l.toLocal(i)
	if r == nil {
		panic(fmt.Sprintf("trying to get oreader for invalid sym %d\n\n", i))
	}
	return l.relocs(r, li)
}

// Relocs返回一个给定本地sym索引和读卡器的Relocs对象。
func (l *Loader) relocs(r *oReader, li uint32) Relocs {
	var rs []goobj.Reloc
	if l.isExtReader(r) {
		pp := l.payloads[li]
		rs = pp.relocs
	} else {
		rs = r.Relocs(li)
	}
	return Relocs{
		rs: rs,
		li: li,
		r:  r,
		l:  l,
	}
}

// FuncInfo提供钩子来访问对象中的goobj.FuncInfo。
type FuncInfo struct {
	l       *Loader
	r       *oReader
	data    []byte
	auxs    []goobj.Aux
	lengths goobj.FuncInfoLengths
}

func (fi *FuncInfo) Valid() bool { return fi.r != nil }

func (fi *FuncInfo) Args() int {
	return int((*goobj.FuncInfo)(nil).ReadArgs(fi.data))
}

func (fi *FuncInfo) Locals() int {
	return int((*goobj.FuncInfo)(nil).ReadLocals(fi.data))
}

func (fi *FuncInfo) FuncID() objabi.FuncID {
	return (*goobj.FuncInfo)(nil).ReadFuncID(fi.data)
}

func (fi *FuncInfo) FuncFlag() objabi.FuncFlag {
	return (*goobj.FuncInfo)(nil).ReadFuncFlag(fi.data)
}

func (fi *FuncInfo) Pcsp() Sym {
	sym := (*goobj.FuncInfo)(nil).ReadPcsp(fi.data)
	return fi.l.resolve(fi.r, sym)
}

func (fi *FuncInfo) Pcfile() Sym {
	sym := (*goobj.FuncInfo)(nil).ReadPcfile(fi.data)
	return fi.l.resolve(fi.r, sym)
}

func (fi *FuncInfo) Pcline() Sym {
	sym := (*goobj.FuncInfo)(nil).ReadPcline(fi.data)
	return fi.l.resolve(fi.r, sym)
}

func (fi *FuncInfo) Pcinline() Sym {
	sym := (*goobj.FuncInfo)(nil).ReadPcinline(fi.data)
	return fi.l.resolve(fi.r, sym)
}

// 在调用各种方法之前，必须先调用Preload
// 以下内容与pcdata、funcdataoff、文件和inltree节点相关。
func (fi *FuncInfo) Preload() {
	fi.lengths = (*goobj.FuncInfo)(nil).ReadFuncInfoLengths(fi.data)
}

func (fi *FuncInfo) Pcdata() []Sym {
	if !fi.lengths.Initialized {
		panic("need to call Preload first")
	}
	syms := (*goobj.FuncInfo)(nil).ReadPcdata(fi.data)
	ret := make([]Sym, len(syms))
	for i := range ret {
		ret[i] = fi.l.resolve(fi.r, syms[i])
	}
	return ret
}

func (fi *FuncInfo) NumFuncdataoff() uint32 {
	if !fi.lengths.Initialized {
		panic("need to call Preload first")
	}
	return fi.lengths.NumFuncdataoff
}

func (fi *FuncInfo) Funcdataoff(k int) int64 {
	if !fi.lengths.Initialized {
		panic("need to call Preload first")
	}
	return (*goobj.FuncInfo)(nil).ReadFuncdataoff(fi.data, fi.lengths.FuncdataoffOff, uint32(k))
}

func (fi *FuncInfo) Funcdata(syms []Sym) []Sym {
	if !fi.lengths.Initialized {
		panic("need to call Preload first")
	}
	if int(fi.lengths.NumFuncdataoff) > cap(syms) {
		syms = make([]Sym, 0, fi.lengths.NumFuncdataoff)
	} else {
		syms = syms[:0]
	}
	for j := range fi.auxs {
		a := &fi.auxs[j]
		if a.Type() == goobj.AuxFuncdata {
			syms = append(syms, fi.l.resolve(fi.r, a.Sym()))
		}
	}
	return syms
}

func (fi *FuncInfo) NumFile() uint32 {
	if !fi.lengths.Initialized {
		panic("need to call Preload first")
	}
	return fi.lengths.NumFile
}

func (fi *FuncInfo) File(k int) goobj.CUFileIndex {
	if !fi.lengths.Initialized {
		panic("need to call Preload first")
	}
	return (*goobj.FuncInfo)(nil).ReadFile(fi.data, fi.lengths.FileOff, uint32(k))
}

// 如果与此FuncInfo关联的函数
// 是一个入口点，这意味着退绕机在撞击时应停止
// 这个功能。
func (fi *FuncInfo) TopFrame() bool {
	return (fi.FuncFlag() & objabi.FuncFlag_TOPFRAME) != 0
}

type InlTreeNode struct {
	Parent   int32
	File     goobj.CUFileIndex
	Line     int32
	Func     Sym
	ParentPC int32
}

func (fi *FuncInfo) NumInlTree() uint32 {
	if !fi.lengths.Initialized {
		panic("need to call Preload first")
	}
	return fi.lengths.NumInlTree
}

func (fi *FuncInfo) InlTree(k int) InlTreeNode {
	if !fi.lengths.Initialized {
		panic("need to call Preload first")
	}
	node := (*goobj.FuncInfo)(nil).ReadInlTree(fi.data, fi.lengths.InlTreeOff, uint32(k))
	return InlTreeNode{
		Parent:   node.Parent,
		File:     node.File,
		Line:     node.Line,
		Func:     fi.l.resolve(fi.r, node.Func),
		ParentPC: node.ParentPC,
	}
}

func (l *Loader) FuncInfo(i Sym) FuncInfo {
	var r *oReader
	var auxs []goobj.Aux
	if l.IsExternal(i) {
		pp := l.getPayload(i)
		if pp.objidx == 0 {
			return FuncInfo{}
		}
		r = l.objs[pp.objidx].r
		auxs = pp.auxs
	} else {
		var li uint32
		r, li = l.toLocal(i)
		auxs = r.Auxs(li)
	}
	for j := range auxs {
		a := &auxs[j]
		if a.Type() == goobj.AuxFuncInfo {
			b := r.Data(a.Sym().SymIdx)
			return FuncInfo{l, r, b, auxs, goobj.FuncInfoLengths{}}
		}
	}
	return FuncInfo{}
}

// 预加载包：添加自动库。
// 不向符号表添加定义的包符号或非包符号。
// 这些都是在LoadSyms中完成的。
// 不读取符号数据。
// 返回对象的指纹。
func (l *Loader) Preload(localSymVersion int, f *bio.Reader, lib *sym.Library, unit *sym.CompilationUnit, length int64) goobj.FingerprintType {
	roObject, readonly, err := f.Slice(uint64(length)) // TODO:无需映射仅用于工具的块（例如RefName）
	if err != nil {
		log.Fatal("cannot read object file:", err)
	}
	r := goobj.NewReaderFromBytes(roObject, readonly)
	if r == nil {
		if len(roObject) >= 8 && bytes.Equal(roObject[:8], []byte("\x00go114ld")) {
			log.Fatalf("found object file %s in old format", f.File().Name())
		}
		panic("cannot read object file")
	}
	pkgprefix := objabi.PathToPrefix(lib.Pkg) + "."
	ndef := r.NSym()
	nhashed64def := r.NHashed64def()
	nhasheddef := r.NHasheddef()
	or := &oReader{
		Reader:       r,
		unit:         unit,
		version:      localSymVersion,
		flags:        r.Flags(),
		pkgprefix:    pkgprefix,
		syms:         make([]Sym, ndef+nhashed64def+nhasheddef+r.NNonpkgdef()+r.NNonpkgref()),
		ndef:         ndef,
		nhasheddef:   nhasheddef,
		nhashed64def: nhashed64def,
		objidx:       uint32(len(l.objs)),
	}

	// 自动库
	lib.Autolib = append(lib.Autolib, r.Autolib()...)

	// 矮化文件表
	nfile := r.NFile()
	unit.FileTable = make([]string, nfile)
	for i := range unit.FileTable {
		unit.FileTable[i] = r.File(i)
	}

	l.addObj(lib.Pkg, or)

	// 调用方希望我们使用所有数据
	f.MustSeek(length, os.SEEK_CUR)

	return r.Fingerprint()
}

// 保存加载程序以及加载符号的临时状态。
type loadState struct {
	l            *Loader
	hashed64Syms map[uint64]symAndSize         // 短散列（内容可寻址）符号，由内容散列键控
	hashedSyms   map[goobj.HashType]symAndSize // 散列（内容可寻址）符号，由内容散列键控
}

// 从对象中预加载给定类型的符号。
func (st *loadState) preloadSyms(r *oReader, kind int) {
	l := st.l
	var start, end uint32
	switch kind {
	case pkgDef:
		start = 0
		end = uint32(r.ndef)
	case hashed64Def:
		start = uint32(r.ndef)
		end = uint32(r.ndef + r.nhashed64def)
	case hashedDef:
		start = uint32(r.ndef + r.nhashed64def)
		end = uint32(r.ndef + r.nhashed64def + r.nhasheddef)
		if l.hasUnknownPkgPath {
			// 内容哈希取决于符号名扩展。如果有包裹
			// 构建时没有完全扩展的名称，内容哈希不可靠。
			// 将它们视为命名符号。
			// 这是罕见的。
			// （对于hashed64Def情况，我们不需要这样做，因为这里有一个hash
			// 函数只是标识函数，它不依赖于
			// 名称扩展。）
			kind = nonPkgDef
		}
	case nonPkgDef:
		start = uint32(r.ndef + r.nhashed64def + r.nhasheddef)
		end = uint32(r.ndef + r.nhashed64def + r.nhasheddef + r.NNonpkgdef())
	default:
		panic("preloadSyms: bad kind")
	}
	l.growAttrBitmaps(len(l.objSyms) + int(end-start))
	needNameExpansion := r.NeedNameExpansion()
	loadingRuntimePkg := r.unit.Lib.Pkg == "runtime"
	for i := start; i < end; i++ {
		osym := r.Sym(i)
		var name string
		var v int
		if kind != hashed64Def && kind != hashedDef { // 对于散列符号，我们不需要名称等
			name = osym.Name(r.Reader)
			if needNameExpansion {
				name = strings.Replace(name, "\"\".", r.pkgprefix, -1)
			}
			v = abiToVer(osym.ABI(), r.version)
		}
		gi := st.addSym(name, v, r, i, kind, osym)
		r.syms[i] = gi
		if osym.Local() {
			l.SetAttrLocal(gi, true)
		}
		if osym.UsedInIface() {
			l.SetAttrUsedInIface(gi, true)
		}
		if strings.HasPrefix(name, "runtime.") ||
			(loadingRuntimePkg && strings.HasPrefix(name, "type.")) {
			if bi := goobj.BuiltinIdx(name, v); bi != -1 {
				// 这是对内置符号的定义。记录它在哪里。
				l.builtinSyms[bi] = gi
			}
		}
		if a := int32(osym.Align()); a != 0 && a > l.SymAlign(gi) {
			l.SetSymAlign(gi, a)
		}
	}
}

// 添加符号、散列（内容可寻址）符号、非包符号和
// 对外部符号（始终命名）的引用。
func (l *Loader) LoadSyms(arch *sys.Arch) {
	// 为符号分配空间，猜测我们需要多少空间。
	// 通过查看上的cmd/compile，根据经验确定此函数
	// Darwin，以及哈希和哈希64符号的选取因子。
	var symSize, hashedSize, hashed64Size int
	for _, o := range l.objs[goObjStart:] {
		symSize += o.r.ndef + o.r.nhasheddef/2 + o.r.nhashed64def/2 + o.r.NNonpkgdef()
		hashedSize += o.r.nhasheddef / 2
		hashed64Size += o.r.nhashed64def / 2
	}
	// 索引0对于符号无效。
	l.objSyms = make([]objSym, 1, symSize)

	l.npkgsyms = l.NSym()
	st := loadState{
		l:            l,
		hashed64Syms: make(map[uint64]symAndSize, hashed64Size),
		hashedSyms:   make(map[goobj.HashType]symAndSize, hashedSize),
	}

	for _, o := range l.objs[goObjStart:] {
		st.preloadSyms(o.r, pkgDef)
	}
	for _, o := range l.objs[goObjStart:] {
		st.preloadSyms(o.r, hashed64Def)
		st.preloadSyms(o.r, hashedDef)
		st.preloadSyms(o.r, nonPkgDef)
	}
	l.nhashedsyms = len(st.hashed64Syms) + len(st.hashedSyms)
	for _, o := range l.objs[goObjStart:] {
		loadObjRefs(l, o.r, arch)
	}
	l.values = make([]int64, l.NSym(), l.NSym()+1000) // +1000为外部符号留出一些空间
}

func loadObjRefs(l *Loader, r *oReader, arch *sys.Arch) {
	// 加载非包引用
	ndef := uint32(r.NAlldef())
	needNameExpansion := r.NeedNameExpansion()
	for i, n := uint32(0), uint32(r.NNonpkgref()); i < n; i++ {
		osym := r.Sym(ndef + i)
		name := osym.Name(r.Reader)
		if needNameExpansion {
			name = strings.Replace(name, "\"\".", r.pkgprefix, -1)
		}
		v := abiToVer(osym.ABI(), r.version)
		r.syms[ndef+i] = l.LookupOrCreateSym(name, v)
		gi := r.syms[ndef+i]
		if osym.Local() {
			l.SetAttrLocal(gi, true)
		}
		if osym.UsedInIface() {
			l.SetAttrUsedInIface(gi, true)
		}
	}

	// 参考包
	npkg := r.NPkg()
	r.pkg = make([]uint32, npkg)
	for i := 1; i < npkg; i++ { // PkgIdx 0是无效的虚拟包
		pkg := r.Pkg(i)
		objidx, ok := l.objByPkg[pkg]
		if !ok {
			log.Fatalf("%v: reference to nonexistent package %s", r.unit.Lib, pkg)
		}
		r.pkg[i] = objidx
	}

	// 包引用的加载标志
	for i, n := 0, r.NRefFlags(); i < n; i++ {
		rf := r.RefFlags(i)
		gi := l.resolve(r, rf.Sym())
		if rf.Flag2()&goobj.SymFlagUsedInIface != 0 {
			l.SetAttrUsedInIface(gi, true)
		}
	}
}

func abiToVer(abi uint16, localSymVersion int) int {
	var v int
	if abi == goobj.SymABIstatic {
		// 静止的
		v = localSymVersion
	} else if abiver := sym.ABIToVersion(obj.ABI(abi)); abiver != -1 {
		// 请注意，数据符号是“ABI0”，它映射到版本0。
		v = abiver
	} else {
		log.Fatalf("invalid symbol ABI: %d", abi)
	}
	return v
}

// 给定符号的ResolveABIAlias返回该符号的ABI别名目标
// 象征如果所讨论的sym不是别名，则sym本身就是别名
// 返回。
func (l *Loader) ResolveABIAlias(s Sym) Sym {
	if l.flags&FlagUseABIAlias == 0 {
		return s
	}
	if s == 0 {
		return 0
	}
	if l.SymType(s) != sym.SABIALIAS {
		return s
	}
	relocs := l.Relocs(s)
	target := relocs.At(0).Sym()
	if l.SymType(target) == sym.SABIALIAS {
		panic(fmt.Sprintf("ABI alias %s references another ABI alias %s", l.SymName(s), l.SymName(target)))
	}
	return target
}

// TopLevelSym测试符号（按名称和种类）以确定
// 符号一级符号（参与链接）或是
// 匿名辅助符号或子符号，其中包含
// 另一个符号。
func (l *Loader) TopLevelSym(s Sym) bool {
	return topLevelSym(l.RawSymName(s), l.SymType(s))
}

// topLevelSym测试符号名称和种类以确定
// 符号一级符号（参与链接）或是
// 匿名辅助符号或子符号，其中包含
// 另一个符号。
func topLevelSym(sname string, skind sym.SymKind) bool {
	if sname != "" {
		return true
	}
	switch skind {
	case sym.SDWARFFCN, sym.SDWARFABSFCN, sym.SDWARFTYPE, sym.SDWARFCONST, sym.SDWARFCUINFO, sym.SDWARFRANGE, sym.SDWARFLOC, sym.SDWARFLINES, sym.SGOFUNC:
		return true
	default:
		return false
	}
}

// cloneToExternal接受现有的对象文件符号（symIdx）
// 并创建一个新的外部符号负载，该负载是具有
// 关于名称、版本、类型、重新定位等。这里的想法
// 如果链接器决定要更新
// 最初作为对象文件的一部分发现的符号
// 如果我们对外部符号进行更新，则更容易做到这一点
// 有效载荷。
func (l *Loader) cloneToExternal(symIdx Sym) {
	if l.IsExternal(symIdx) {
		panic("sym is already external, no need for clone")
	}

	// 从物体上读出细节。
	r, li := l.toLocal(symIdx)
	osym := r.Sym(li)
	sname := osym.Name(r.Reader)
	if r.NeedNameExpansion() {
		sname = strings.Replace(sname, "\"\".", r.pkgprefix, -1)
	}
	sver := abiToVer(osym.ABI(), r.version)
	skind := sym.AbiSymKindToSymKind[objabi.SymKind(osym.Type())]

	// 创建新符号，更新版本和种类。
	pi := l.newPayload(sname, sver)
	pp := l.payloads[pi]
	pp.kind = skind
	pp.ver = sver
	pp.size = int64(osym.Siz())
	pp.objidx = r.objidx

	// 如果这是def，则复制内脏。我们期待这个案子
	// 非常罕见（可能出现的一种情况是-X）。
	if li < uint32(r.NAlldef()) {

		// 复制重定位
		relocs := l.Relocs(symIdx)
		pp.relocs = make([]goobj.Reloc, relocs.Count())
		for i := range pp.relocs {
			// 复制relocs切片。
			// 将本地引用转换为全局引用。
			rel := relocs.At(i)
			pp.relocs[i].Set(rel.Off(), rel.Siz(), uint16(rel.Type()), rel.Add(), goobj.SymRef{PkgIdx: 0, SymIdx: uint32(rel.Sym())})
		}

		// 复制数据
		pp.data = r.Data(li)
	}

	// 如果要覆盖数据符号，请收集关联的
	// Gotype，以便将其传播到新符号。
	auxs := r.Auxs(li)
	pp.auxs = auxs

	// 将新的有效负载安装到全局索引空间。
	// （正如上面的访问器一样，这需要在末尾发生
	// 需要访问旧的符号内容。）
	l.objSyms[symIdx] = objSym{l.extReader.objidx, uint32(pi)}
	l.extReader.syms = append(l.extReader.syms, symIdx)
}

// 将符号src的有效负载复制到dst。src和dst都必须是外部的
// 符号。
// 预期用例是，在构建/链接共享库时，
// 在进行符号名称损坏时，Go对象文件可能引用
// 原始符号名称，而共享库提供了具有
// 被弄坏的名字。当我们进行损坏时，我们将损坏的负载复制到原始负载。
func (l *Loader) CopySym(src, dst Sym) {
	if !l.IsExternal(dst) {
		panic("dst is not external") // l、 newExtSym（l.SymName（dst），l.SymVersion（dst））
	}
	if !l.IsExternal(src) {
		panic("src is not external") // l、 克隆外部（src）
	}
	l.payloads[l.extIndex(dst)] = l.payloads[l.extIndex(src)]
	l.SetSymPkg(dst, l.SymPkg(src))
	// TODO:其他属性？
}

// CopyAttributes将符号“src”的所有属性复制到
// 符号“dst”。
func (l *Loader) CopyAttributes(src Sym, dst Sym) {
	l.SetAttrReachable(dst, l.AttrReachable(src))
	l.SetAttrOnList(dst, l.AttrOnList(src))
	l.SetAttrLocal(dst, l.AttrLocal(src))
	l.SetAttrNotInSymbolTable(dst, l.AttrNotInSymbolTable(src))
	if l.IsExternal(dst) {
		l.SetAttrVisibilityHidden(dst, l.AttrVisibilityHidden(src))
		l.SetAttrDuplicateOK(dst, l.AttrDuplicateOK(src))
		l.SetAttrShared(dst, l.AttrShared(src))
		l.SetAttrExternal(dst, l.AttrExternal(src))
	} else {
		// 某些属性只能针对外部符号进行修改。
		// 在这种情况下，不要试图转移属性
		// 即使发生冲突，也要从源代码中删除。这就来了
		// 将属性从dupOK ABI包装符号复制到
		// 实际目标符号（可能未标记为dupOK）。
	}
	l.SetAttrSpecial(dst, l.AttrSpecial(src))
	l.SetAttrCgoExportDynamic(dst, l.AttrCgoExportDynamic(src))
	l.SetAttrCgoExportStatic(dst, l.AttrCgoExportStatic(src))
	l.SetAttrReadOnly(dst, l.AttrReadOnly(src))
}

// CreateExtSym使用指定的名称创建新的外部符号
// 不将其添加到任何查找表中，返回其Sym索引。
func (l *Loader) CreateExtSym(name string, ver int) Sym {
	return l.newExtSym(name, ver)
}

// CreateStaticSym使用指定的名称创建新的静态符号
// 不将其添加到任何查找表中，返回其Sym索引。
func (l *Loader) CreateStaticSym(name string) Sym {
	// 指定一个新的唯一负片版本--这是为了标记
	// 符号，使其不包括在名称查找表中。
	l.anonVersion--
	return l.newExtSym(name, l.anonVersion)
}

func (l *Loader) FreeSym(i Sym) {
	if l.IsExternal(i) {
		pp := l.getPayload(i)
		*pp = extSymPayload{}
	}
}

// relocId本质上是一个标识Rth的<S，R>元组
// 符号S的重新定位。
type relocId struct {
	sym  Sym
	ridx int
}

// SetRelocVariant在上设置重定位的“variant”属性
// 一些特定的符号。
func (l *Loader) SetRelocVariant(s Sym, ri int, v sym.RelocVariant) {
	// 健康检查
	if relocs := l.Relocs(s); ri >= relocs.Count() {
		panic("invalid relocation ID")
	}
	if l.relocVariant == nil {
		l.relocVariant = make(map[relocId]sym.RelocVariant)
	}
	if v != 0 {
		l.relocVariant[relocId{s, ri}] = v
	} else {
		delete(l.relocVariant, relocId{s, ri})
	}
}

// RelocVariant返回上重新定位的“variant”属性
// 一些特定的符号。
func (l *Loader) RelocVariant(s Sym, ri int) sym.RelocVariant {
	return l.relocVariant[relocId{s, ri}]
}

// UnfinedRelocTargets遍历全局符号索引
// 空间，查找重定位目标为未定义的符号
// 参考资料。链接器的loadlib方法使用它来确定
// 系统库中存在未解析的函数引用
// （例如，libgcc.a），可能是由于CGO代码。回来
// 值是对应于未定义的
// 交叉参考。“limit”参数控制最大的
// 返回结果；如果“limit”为-1，则返回所有未定义项。
func (l *Loader) UndefinedRelocTargets(limit int) []Sym {
	result := []Sym{}
	for si := Sym(1); si < Sym(len(l.objSyms)); si++ {
		relocs := l.Relocs(si)
		for ri := 0; ri < relocs.Count(); ri++ {
			r := relocs.At(ri)
			rs := r.Sym()
			if rs != 0 && l.SymType(rs) == sym.SXREF && l.RawSymName(rs) != ".got" {
				result = append(result, rs)
				if limit != -1 && len(result) >= limit {
					break
				}
			}
		}
	}
	return result
}

// AssignTextSymbolOrder在每个
// 库和编译单元，确保已放置包
// 按照依赖顺序（首先是内部的，然后是所有其他的）。返回值
// 是所有文本符号的一部分。
func (l *Loader) AssignTextSymbolOrder(libs []*sym.Library, intlibs []bool, extsyms []Sym) []Sym {

	// 此时库Textp列表应为空。
	for _, lib := range libs {
		if len(lib.Textp) != 0 {
			panic("expected empty Textp slice for library")
		}
		if len(lib.DupTextSyms) != 0 {
			panic("expected empty DupTextSyms slice for library")
		}
	}

	// 用于记录分配给某个单元的dupok符号。
	// 无法在此处使用onlist属性，因为它需要
	// 清除此选项，以便稍后将符号分配给单元。
	// 注：一旦我们不再需要，我们可以转换为使用onList
	// 调用常规addToTextp。
	assignedToUnit := MakeBitmap(l.NSym() + 1)

	// 使用可访问的外部符号启动textp。
	textp := []Sym{}
	for _, sym := range extsyms {
		if !l.attrReachable.Has(sym) {
			continue
		}
		textp = append(textp, sym)
	}

	// 浏览Go对象文件中的所有文本符号并附加
	// 将它们添加到相应库的textp列表中。
	for _, o := range l.objs[goObjStart:] {
		r := o.r
		lib := r.unit.Lib
		for i, n := uint32(0), uint32(r.NAlldef()); i < n; i++ {
			gi := l.toGlobal(r, i)
			if !l.attrReachable.Has(gi) {
				continue
			}
			osym := r.Sym(i)
			st := sym.AbiSymKindToSymKind[objabi.SymKind(osym.Type())]
			if st != sym.STEXT {
				continue
			}
			dupok := osym.Dupok()
			if r2, i2 := l.toLocal(gi); r2 != r || i2 != i {
				// dupok文本符号解析为另一个包。
				// 我们仍然需要记录它在当前环境中的存在
				// 打包，就像蹦床通行证要求打包一样
				// 按依赖顺序排列。
				lib.DupTextSyms = append(lib.DupTextSyms, sym.LoaderSym(gi))
				continue // 不同对象中的符号
			}
			if dupok {
				lib.DupTextSyms = append(lib.DupTextSyms, sym.LoaderSym(gi))
				continue
			}

			lib.Textp = append(lib.Textp, sym.LoaderSym(gi))
		}
	}

	// 现在组装全局textp，并将文本符号指定给单位。
	for _, doInternal := range [2]bool{true, false} {
		for idx, lib := range libs {
			if intlibs[idx] != doInternal {
				continue
			}
			lists := [2][]sym.LoaderSym{lib.Textp, lib.DupTextSyms}
			for i, list := range lists {
				for _, s := range list {
					sym := Sym(s)
					if !assignedToUnit.Has(sym) {
						textp = append(textp, sym)
						unit := l.SymUnit(sym)
						if unit != nil {
							unit.Textp = append(unit.Textp, s)
							assignedToUnit.Set(sym)
						}
						// Dupok符号可以在多个包中定义；这个
						// 选择dupok sym的关联包
						// 任意（链接器包含的第一个包）
						// 负载）。将其Pkg规范化为与之一起使用的包
						// 它将以文本形式予以规定。
						if i == 1 /* DupTextSyms2 */ && l.SymPkg(sym) != lib.Pkg {
							l.SetSymPkg(sym, lib.Pkg)
						}
					}
				}
			}
			lib.Textp = nil
			lib.DupTextSyms = nil
		}
	}

	return textp
}

// ErrorReporter是用于报告错误的帮助器类。
type ErrorReporter struct {
	ldr              *Loader
	AfterErrorAction func()
}

// Errorf方法记录一条错误消息。
// None
// 每次出错后，都会调用error actions函数；这
// 将立即终止链接（如果给定-h选项）
// 或者，如果打印了超过20个错误，它将保留计数并退出。
// None
// 记录错误意味着退出cmd/link时将删除任何
// 输出文件并返回非零错误代码。
// None
func (reporter *ErrorReporter) Errorf(s Sym, format string, args ...interface{}) {
	if s != 0 && reporter.ldr.SymName(s) != "" {
		format = reporter.ldr.SymName(s) + ": " + format
	} else {
		format = fmt.Sprintf("sym %d: %s", s, format)
	}
	format += "\n"
	fmt.Fprintf(os.Stderr, format, args...)
	reporter.AfterErrorAction()
}

// GetErrorReporter返回加载程序的关联错误报告程序。
func (l *Loader) GetErrorReporter() *ErrorReporter {
	return l.errorReporter
}

// Errorf方法记录一条错误消息。有关详细信息，请参阅ErrorReporter.Errorf。
func (l *Loader) Errorf(s Sym, format string, args ...interface{}) {
	l.errorReporter.Errorf(s, format, args...)
}

// 符号统计。
func (l *Loader) Stat() string {
	s := fmt.Sprintf("%d symbols, %d reachable\n", l.NSym(), l.NReachableSym())
	s += fmt.Sprintf("\t%d package symbols, %d hashed symbols, %d non-package symbols, %d external symbols\n",
		l.npkgsyms, l.nhashedsyms, int(l.extStart)-l.npkgsyms-l.nhashedsyms, l.NSym()-int(l.extStart))
	return s
}

// 用于调试。
func (l *Loader) Dump() {
	fmt.Println("objs")
	for _, obj := range l.objs[goObjStart:] {
		if obj.r != nil {
			fmt.Println(obj.i, obj.r.unit.Lib)
		}
	}
	fmt.Println("extStart:", l.extStart)
	fmt.Println("Nsyms:", len(l.objSyms))
	fmt.Println("syms")
	for i := Sym(1); i < Sym(len(l.objSyms)); i++ {
		pi := ""
		if l.IsExternal(i) {
			pi = fmt.Sprintf("<ext %d>", l.extIndex(i))
		}
		sect := ""
		if l.SymSect(i) != nil {
			sect = l.SymSect(i).Name
		}
		fmt.Printf("%v %v %v %v %x %v\n", i, l.SymName(i), l.SymType(i), pi, l.SymValue(i), sect)
	}
	fmt.Println("symsByName")
	for name, i := range l.symsByName[0] {
		fmt.Println(i, name, 0)
	}
	for name, i := range l.symsByName[1] {
		fmt.Println(i, name, 1)
	}
	fmt.Println("payloads:")
	for i := range l.payloads {
		pp := l.payloads[i]
		fmt.Println(i, pp.name, pp.ver, pp.kind)
	}
}
